
#include <cstdio>
#include <BOpenGL>
#include <BPainter>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BVBoxLayout>
#include <BLabel>

#include "PixmapArea.h"

PixmapArea::PixmapArea(BWidget* parent)
{
	this->setStyleSheet("background:36, 36, 36");

	this->setAreaAlign(Align_Center);

	_pixmap = new BPixmap();
	_pixmap->setSamples(0);
	_pixmap->setFilter(BTexture::Filter_Nearest);

	_image = new BImage(Format_RGBA);
	_darkness = 128;
	_draging = false;
	_zoom = 100;
	_hoveredLayer = 0;

	_canvas = new BImage(_pixmap->size(), Format_Gray);
	_canvas->setFilter(BTexture::Filter_Nearest);

	connect(_pixmap, Signal_Dirty, &PixmapArea::slotPixmapDirty);
	connect(_pixmap, Signal_Framed, &PixmapArea::slotPixmapFramed);
	connect(_pixmap, Signal_Resized, &PixmapArea::slotPixmapResized);

}
PixmapArea::~PixmapArea()
{

}

BPixmap* PixmapArea::pixmap() const
{
	return _pixmap;
}
BImage* PixmapArea::image() const
{
	return _image;
}

ImageLayer* PixmapArea::findLayer(const BImage* image) const
{
	for (int i = 0; i < _pixmap->widgetCount(); i++)
	{
		ImageLayer* layer = (ImageLayer*)_pixmap->widget(i);
		if (layer->image() == image)
			return layer;
	}
	return 0;
}

void PixmapArea::enableEvent()
{
	_eventEnabled = true;
}
void PixmapArea::disableEvent()
{
	_eventEnabled = false;
}
bool PixmapArea::eventEnabled()
{
	return 	_eventEnabled;
}

void PixmapArea::setZoom(int zoom)
{
	zoom = bClamp(zoom, 10, 1000);
	if (_zoom != zoom)
	{
		_zoom = zoom;
		adjustArea();
		this->fresh();
		emit("zoom-changed", _zoom);
	}
}
int PixmapArea::zoom() const
{
	return _zoom;
}

void PixmapArea::setAreaImage(BImage* areaImage)
{
	if (_areaImage != areaImage)
	{
		_areaImage = areaImage;
		this->fresh();
	}
}
BImage* PixmapArea::areaImage() const
{
	return _areaImage;
}

void PixmapArea::setGridDrakness(BByte darkness)
{
	_darkness = darkness;
	freshCanvas();
}
BByte PixmapArea::gridDarkness() const
{
	return _darkness;
}

void PixmapArea::select(BWidget* widget)
{
	if (widget && !_selectedWidgets.contain(widget))
	{
		_selectedWidgets.insert(widget);
		this->fresh();
		this->emit(Signal_SelectionChanged, _selectedWidgets);
	}
}
void PixmapArea::unselect(BWidget* widget)
{
	if (_selectedWidgets.remove(widget))
	{
		this->emit(Signal_SelectionChanged, _selectedWidgets);
		this->fresh();
	}
}
void PixmapArea::clearSelection()
{
	if (_selectedWidgets.size())
	{
		while (BWidget* widget = _selectedWidgets.last())
		{
			_selectedWidgets.remove(_selectedWidgets.size() - 1, 1);
		}
		this->emit(Signal_SelectionChanged);
		this->fresh();
	}
}

BWidgetSet& PixmapArea::selectedWidgets()
{
	return _selectedWidgets;
}
const BWidgetSet& PixmapArea::selectedWidgets() const
{
	return _selectedWidgets;
}

BRect PixmapArea::mapToPixmap(const BRect& rect) const
{
	double ratio = _zoom / 100.0;
	BRect res = (rect - this->area().pos()) / ratio;
	return res;
}
BRect PixmapArea::mapFromPixmap(const BRect& rect) const
{
	double ratio = _zoom / 100.0;
	BRect res = rect * ratio + this->area().pos();
	return res;
}

BPoint PixmapArea::mapToPixmap(const BPoint& point) const
{
	double ratio = _zoom / 100.0;
	BPoint res = (point - this->area().pos()) / ratio;
	return res;
}
BPoint PixmapArea::mapFromPixmap(const BPoint& point) const
{
	double ratio = _zoom / 100.0;
	BPoint res = point * ratio + this->area().pos();
	return res;
}

void PixmapArea::adjustArea()
{
	if (_pixmap)
	{
		BSize size = _pixmap->size();
		if (_zoom != 100)
		{
			float ratio = _zoom / 100.0f;
			size *= ratio;
		}
		BRect area = this->area();
		if (this->area().size() != size)
		{
			area.setSize(size);
			this->setArea(area);
		}
	}
}
void PixmapArea::freshCanvas()
{
	BByte* data = _canvas->pixels()->data();
	int size = _canvas->pixels()->size();
	for (int r = 0; r < _canvas->height(); r++)
	{
		int pos = r * _canvas->width();
		if (_darkness)
		{
			BByte fore = r % 2 ? 255 : _darkness;
			BByte back = r % 2 ? _darkness : 255;
			for (int c = 0; c < _canvas->width(); c++)
			{
				if (c % 2)
					data[pos + c] = back;
				else
					data[pos + c] = fore;
			}
		}
		else
		{
			for (int c = 0; c < _canvas->width(); c++)
			{
				data[pos + c] = 255;
			}
		}
	}
	_canvas->dirty();
	_pixmap->dirty();
}
BString PixmapArea::findValidName(const BString& name)
{
	bool exist = false;
	for (int i = 0; i < _pixmap->widgetCount(); i++)
	{
		if (_pixmap->widget(i)->title() == name)
		{
			exist = true;
			break;
		}
	}
	if (!exist)
		return name;
	BString realName;
	int number = 0;
	while (number < 9999)
	{
		realName.reset();
		realName << name << number++;
		bool exist = false;
		for (int i = 0; i < _pixmap->widgetCount(); i++)
		{
			if (_pixmap->widget(i)->title() == realName)
			{
				exist = true;
				break;
			}
		}
		if (!exist)
			break;
	}
	return realName;
}

void PixmapArea::slotPixmapResized()
{
	_canvas->setSize(_pixmap->size());
	freshCanvas();
	adjustArea();
}
void PixmapArea::slotPixmapDirty()
{
	this->fresh();
}
void PixmapArea::slotPixmapFramed()
{
	_pixmap->capture(_image);
	_image->reverse(Orientation_Vertical);
}

void PixmapArea::scrolling(Orientation orientation)
{
	this->fresh();
}

void PixmapArea::event(const BEvent& event)
{
	if (_eventEnabled)
	{
		double ratio = _zoom / 100.0;
		if (event.type() >= Event_MouseMove && event.type() <= Event_DoubleClick)
		{
			BMouseEvent mouseEvent(event);
			BPoint point = (mouseEvent.pos() - this->area().pos()) * ratio;
			mouseEvent.setGlobalPos(point);
			mouseEvent.setAbsolutePos(point);
			_pixmap->event(mouseEvent);
			if (event.accepted())
				return;
		}
		else
		{
			_pixmap->event(event);
			if (event.accepted())
				return;
		}
	}
	if (event.type() == Event_Update || event.type() == Event_Fresh)
	{
		_pixmap->event(event);
	}
	BScrollArea::event(event);
}

void PixmapArea::resizeEvent(const BEvent& event)
{
	BSize size = event.value();
	adjustArea();
	BScrollArea::resizeEvent(event);
}

void PixmapArea::freshEvent(const BEvent& event)
{
	BScrollArea::freshEvent(event);
	BRect area = this->area();
	float sw = area.width() / (float)_pixmap->width();
	float sh = area.height() / (float)_pixmap->height();
	_canvas->setEnabled(sw > 2 && sh > 2);
}
void PixmapArea::paintEvent(const BEvent& event)
{
	BScrollArea::paintEvent(event);

	BPainter painter(this);
	painter.drawBackground();
	painter.drawPerch();
	painter.drawMargin();
	painter.drawBorder();

	const BRect& crect = this->centerRect();
	painter.setClip(crect);

	BRect area = this->area();
	painter.setColor(Color_Text);
	painter.drawRect(area);

	if (_canvas->enabled())
	{
		painter.drawImage(this->area(), _canvas);
	}
	else
	{
		painter.setColor(255, 255, 255);
		painter.fillRect(this->area());
	}

	if (_areaImage)
	{
		painter.drawImage(this->area(), _areaImage);
	}

	painter.bind(_pixmap);
	painter.fillRect(this->area());
	painter.unbind();

	float ratio = _zoom / 100.0f;
	if (_hoveredLayer)
	{
		BRect rect = this->mapFromPixmap(_hoveredLayer->rect());
		painter.setLineStipple(0x0F0F);
		painter.setColor(Color_Handle);
		painter.drawRect(rect);
		painter.setLineStipple(0);
	}

	if (_selectedWidgets.size())
	{
		for (int i = 0; i < _selectedWidgets.size(); i++)
		{
			BWidget* layer = _selectedWidgets[i];
			BRect rect = layer->rect() * ratio + this->area().pos();
			rect.left() += 1;
			rect.bottom() -= 1;
			painter.setLineStipple(0xFF00);
			painter.setColor(Color_Handle, State_Checked);
			painter.drawRect(rect);
			painter.setLineStipple(0);
		}
	}
}

void PixmapArea::keyPressEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_Delete)
	{
		if (_selectedWidgets.size())
		{
			//RemoveOpera* opera = new RemoveOpera();
			for (int i = 0; i < _selectedWidgets.size(); i++)
			{
				BWidget* layer = _selectedWidgets[i];
				_pixmap->removeWidget(layer);
				_selectedWidgets.remove(i--);
				this->emit(Signal_Remove, layer);
				//opera->addLayer(layer);
			}
			//emit("opera", opera);
			this->emit(Signal_SelectionChanged);
			_hoveredLayer = 0;
		}
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_Escape)
	{
		for (int i = 0; i < _selectedWidgets.size(); i++)
		{
			BWidget* layer = _selectedWidgets[i];
			_selectedWidgets.remove(i--);
		}
		this->emit(Signal_SelectionChanged);
		keyEvent.accept();
	}
	BPoint offset;
	if (keyEvent.key() == Key_Up)
		offset.y() -= 1;
	else if (keyEvent.key() == Key_Down)
		offset.y() += 1;
	else if (keyEvent.key() == Key_Left)
		offset.x() -= 1;
	else if (keyEvent.key() == Key_Right)
		offset.x() += 1;
	if (offset != BPoint())
	{
		BWidgetSet selectedWidgets = this->selectedWidgets();
		for (int i = 0; i < selectedWidgets.size(); i++)
		{
			BWidget* layer = selectedWidgets[i];
			layer->move(offset);
		}
	}

	BScrollArea::keyPressEvent(keyEvent);
}

void PixmapArea::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	BScrollArea::mouseEnterEvent(mouseEvent);
	if (_hoveredLayer)
	{
		_hoveredLayer = 0;
		this->fresh();
	}
}
void PixmapArea::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	BScrollArea::mouseLeaveEvent(mouseEvent);
	if (_hoveredLayer)
	{
		_hoveredLayer = 0;
		this->fresh();
	}
}
void PixmapArea::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BScrollArea::mousePressEvent(mouseEvent);
	if (mouseEvent.button() == Button_Left)
	{
		if (mouseEvent.keyState(Key_Ctrl))
		{
			if (_hoveredLayer)
			{
				select(_hoveredLayer);
			}
		}
		else
		{
			bool changed = false;
			for (int i = 0; i < _selectedWidgets.size(); i++)
			{
				BWidget* widget = _selectedWidgets[i];
				if (widget == _hoveredLayer)
					continue;
				_selectedWidgets.remove(i--);
				changed = true;
			}
			if (_hoveredLayer && !_selectedWidgets.contain(_hoveredLayer))
			{
				_selectedWidgets.insert(_hoveredLayer);
				changed = true;
			}
			if (changed)
			{
				this->emit(Signal_SelectionChanged, _selectedWidgets);
			}
		}
		this->fresh();
		return;
	}
	if (mouseEvent.button() == Button_Middle)
	{
		_draging = true;
		_dragPoint = mouseEvent.pos();
		_dragArea = this->area();
		this->setCursor(Cursor_SizeAll);
	}
}
void PixmapArea::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	BScrollArea::mouseReleaseEvent(mouseEvent);
	if (_draging)
	{
		_draging = false;
		this->setCursor(Cursor_None);
	}
}

void PixmapArea::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BScrollArea::mouseMoveEvent(mouseEvent);

	const BPoint& pos = mouseEvent.pos();
	const BRect& area = this->area();
	if (mouseEvent.button() == Button_Middle)
	{
		BRect area = _dragArea + (pos - _dragPoint);
		this->setArea(area);
		this->fresh();
		return;
	}
	BPoint point = mapToPixmap(pos);
	emit("area-point", point);
	BWidget* hoveredLayer = (BWidget*)_pixmap->widgetAt(point);
	if (_hoveredLayer != hoveredLayer)
	{
		_hoveredLayer = hoveredLayer;
		this->fresh();
	}
	if (hoveredLayer)
	{
		point -= hoveredLayer->pos();
		emit("layer-point", point);
	}
}
void PixmapArea::mouseWheelEvent(const BMouseEvent& mouseEvent)
{
	BScrollArea::mouseWheelEvent(mouseEvent);

	const BPoint& pos = mouseEvent.pos();
	BRect area = this->area();
	double xratio = (pos.x() - area.x()) / (double)area.width();
	double yratio = (pos.y() - area.y()) / (double)area.height();
	if (mouseEvent.wheel() > 0)
	{
		int zoom = _zoom + 10;
		this->setZoom(zoom);
	}
	if (mouseEvent.wheel() < 0)
	{
		int zoom = _zoom - 10;
		this->setZoom(zoom);
	}
	BRect rect = this->area();
	BPoint point(int(xratio * rect.width() + rect.x()), int(yratio * rect.height() + rect.y()));
	rect.move(pos - point);
	this->setArea(rect);
	mouseEvent.accept();
}

void PixmapArea::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	BScrollArea::mouseClickEvent(mouseEvent);
}
void PixmapArea::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	if (_hoveredLayer)
	{
		this->emit("layer-double-clicked", _hoveredLayer);
		return;
	}
	BScrollArea::mouseDoubleClickEvent(mouseEvent);
}
